using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ObviousCode.Stately;
using System.IO;
using ICSharpCode.NRefactory.CSharp;
using StatelyCompiler;
using ObviousCode.Stately.Compiler.Validation.Expectations;
using ObviousCode.Stately.Compiler.Validation.Expectations.ExpectationValidation;
using ObviousCode.Stately.Compiler.Parser;

namespace ObviousCode.Stately.Compiler.Validation
{
    public partial class CodeFileExpectationValidator
    {
        public void Start(ValidationContext validationContext, Action<ExpectationValidatorContext> callback)
        {
            ExpectationValidatorContext context = new ExpectationValidatorContext();

            context.Callback = callback;
            context.CodeFileName = validationContext.PrimaryCodeFileCandidate;
            context.Classname = validationContext.Classname;
            context.Namespace = validationContext.Namespace;
            context.DataContext = validationContext.DataContext;
            context.DefinedTransitions = validationContext.DefinedTransitions;
            

            _stately.Start(Step.ParseFile, context);
        }

        private ITransitionRequest HandleParseFile(ExpectationValidatorContext context)
        {
            using (FileStream stream = File.OpenRead(context.CodeFileName))
            {
                SyntaxTree tree = new CSharpParser().Parse(stream);

                context.ParsedSyntaxTree = tree;

                context.Callback(context);
            }

            return _stately.GetRequest(Step.LocateNamespaceElement, context);
        }

        private ITransitionRequest HandleLocateNamespaceElement(ExpectationValidatorContext context)
        {
            AstNode node = context
                .ParsedSyntaxTree
                .Children
                .Where(c => c.Is<NamespaceDeclaration>())
                .Where(c => c.As<NamespaceDeclaration>().FullName == context.Namespace)
                .FirstOrDefault();

            if (node == null)
            {
                _stately.Derail(Step.NotifyUnableToLocateNamespaceElement, context);
                return HaltRequest.Instance;
            }

            context.NamespaceElement = node.As<NamespaceDeclaration>();

            return _stately.T(Step.LocateClassElement, context);
        }

        private ITransitionRequest HandleLocateClassElement(ExpectationValidatorContext context)
        {
            AstNode typeDeclaration = context
                .NamespaceElement
                .Children
                .Where(c => c.Is<TypeDeclaration>())
                .Where(c => c.As<TypeDeclaration>().Name == context.Classname)
                .FirstOrDefault();

            if (typeDeclaration == null)
            {
                _stately.Derail(Step.NotifyUnableToLocateClassElement, context);
                return HaltRequest.Instance;
            }

            context.ClassElement = typeDeclaration.As<TypeDeclaration>();
            
            return _stately.T(Step.AssertRelevantMethods, context);
        }

        public ITransitionRequest HandleAssertRelevantMethods(ExpectationValidatorContext context)
        {
            foreach (DefinedStateTransition transition in context.DefinedTransitions)
            {
                MethodDeclaration method =
                    context.ClassElement
                             .Children
                             .Where(c => c.Is<MethodDeclaration>())
                             .Where(c => c.As<MethodDeclaration>().Name == transition.ExpectedMethodName)
                             .Select(c => c.As<MethodDeclaration>())
                             .FirstOrDefault();

                if (method == null) { context.Errors.Add(ValidationError.MissingMethodError(transition)); }
                else
                {
                    AssertAllTransitionsAreHandled(method.Body, transition, context);
                }
            }

            return CompleteRequest.Instance;
        }

        private void AssertAllTransitionsAreHandled(BlockStatement statement, DefinedStateTransition definedTransition, ExpectationValidatorContext context)
        {
            DefinedTransitionValidator validator = new DefinedTransitionValidator();

            validator.Validate(statement, definedTransition, context, (c) =>
            {

            });
        }


        private static void AssertStartStatement(BlockStatement statement, DefinedStateTransition definedTransition, ExpectationValidatorContext context)
        {
            List<InvocationExpression> invocations =
                statement
                .Children
                .Where(c => c.Is<ExpressionStatement>())
                .Where(c => c.As<ExpressionStatement>().Expression.Is<InvocationExpression>())
                .Select(c => c.As<ExpressionStatement>().Expression.As<InvocationExpression>())
                .ToList();

            foreach (Transition transition in definedTransition.ExpectedTransitions)
            {

            }
        }

        public event EventHandler NotifyUnableToLocateNamespaceElement;
        private ITransitionRequest HandleNotifyUnableToLocateNamespaceElement(ExpectationValidatorContext context)
        {
            if (NotifyUnableToLocateNamespaceElement != null) NotifyUnableToLocateNamespaceElement(this, EventArgs.Empty);

            else
            {
                throw new InvalidOperationException("Unable to locate namespace element in Syntax Tree");
            }

            return HaltRequest.Instance;
        }

        public event EventHandler NotifyUnableToLocateClassElement;
        private ITransitionRequest HandleNotifyUnableToLocateClassElement(ExpectationValidatorContext context)
        {

            if (NotifyUnableToLocateClassElement != null) NotifyUnableToLocateClassElement(this, EventArgs.Empty);

            else
            {
                throw new InvalidOperationException("Unable to locate class element in Syntax Tree");
            }

            return HaltRequest.Instance;

        }

    }
}

